package edu.gatech.spacetraderteam3.view;

import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.image.BufferedImage;

/**
 * A class that represents horizontal and vertical sliders in the GUI
 * @author Alexander Ikonomidis and Ashutosh Gupta
 * @version 1.0
 */
public class SGGSlider extends SGGComponent {

    /**
     * The number of clicks tolerated by this component
     */
    private static final int CLICK_TOLERANCE = 10;

    /**
     * The minimum and max values for the slider
     */
    private int min = 0, max = 100;

    /**
     * The slider's length in pixels
     */
    private final int length;

    /**
     * The end and middle segment pictures of the slider lines
     */
    private final BufferedImage end, repeat;

    /**
     * Dictates whether the slider is horizontal or vertical
     */
    private final boolean vertical;

    /**
     * The sliding button
     */
    private final SGGButton slider;

    /**
     * Constructor for the slider
     * @param length Set's length of the slider
     * @param vertical Whether the slider is vertical 
     * @param min Minimum value of the slider
     * @param max Maximum value of the slider
     * @param value Default value of the slider
     */
    protected SGGSlider(int length, boolean vertical, int min, int max, int value) {
        super(0, 0);
        this.vertical = vertical;
        this.length = length;
        this.min = min;
        this.max = max;
        slider = new SGGButton(Assets.ButtonType.SLIDER);

        end = Assets.MiscImage.SLIDER_END.image();
        repeat = Assets.MiscImage.SLIDER_REPEAT.image();

        if (vertical) {
            setDimensions(CLICK_TOLERANCE, length);
        } else {
            setDimensions(length, CLICK_TOLERANCE);
        }
        setValue(value);
    }

    /**
     * Paints the slider. 
     * @param g Graphics component to paint onto
     */
    public void doPaint(Graphics2D g) {
        if (vertical) {
            slider.setOrigin(getX() - 5, getY());
            g.rotate(Math.PI / 2);

            // end
            g.drawImage(end, getY(), -getX() - (slider.getWidth() / 2 + 4) + 5,
                        end.getWidth(), end.getHeight() + 4, null);
            // repeat
            g.drawImage(repeat, getY() + end.getHeight(), -getX()
                                - (slider.getWidth() / 2 + 4) + 5, repeat.getWidth()
                                * length,
                        repeat.getHeight() + 4, null);
            // end
            g.rotate(Math.PI);
            g.drawImage(end, -getY() - repeat.getWidth() * length - end.getHeight() * 2,
                        getX() - end.getWidth() - 4 + (slider.getWidth() / 2 + 4) - 5,
                        end.getWidth(), end.getHeight() + 4, null);
            g.rotate(-Math.PI);

            g.rotate(-Math.PI / 2);
        } else {
            slider.setOrigin(getX(), getY());
            // end
            g.drawImage(end, getX(), getY(), end.getWidth(), getHeight(), null);
            // repeat
            g.drawImage(repeat, getX() + end.getWidth(), getY(), repeat.getWidth()
                    * length - end.getWidth() * 2, getHeight(), null); // end.width*2 because length is the total length of the component
            // end
            g.rotate(Math.PI);
            g.drawImage(end, -getX() - repeat.getWidth() * getWidth(), -getY()
                    - getHeight(), end.getWidth(), getHeight(), null);
            g.rotate(-Math.PI);
        }
        slider.paint(g);
    }

    /**
     * Get's the value of the slider
     * @return Slider value
     */
    public int getValue() {
        double percent = 0;
        if (vertical) {
            percent = 1.0 - Math.max(0, (1.0 * slider.getY() - getY())
                    / (getHeight() - slider.getHeight() / 2));
        } else {
            percent = Math.max(0,
                               (1.0 * slider.getX() - getX())
                                       / (getWidth() - slider.getWidth() / 2));
        }
        return (int) ((max - min) * percent + min);
    }

    /**
     * Sets the value of the slider
     * @param value Slider value to set
     */
    public void setValue(int value) { // TODO: Figure out why setValue is not working for vertical Slider
        final double percent = (1.0 * value - min) / (max - min);
        if (percent >= 0 && percent <= 1) {
            if (vertical) {
                int newY = (int) (getY() + (length - slider.getHeight() / 2)
                        * (1.0 - percent));
                newY = Math.max(-slider.getHeight() / 2,
                                Math.min(getHeight() - slider.getHeight() / 2, newY
                                        - getY() - slider.getHeight() / 2));
                setSliderPosition(0, newY);
            } else {
                final int newX = (int) (getX() + (length - slider.getWidth() / 2)
                        * percent);
                setSliderPosition(newX, -slider.getHeight() * 2 / 5);
            }
        }
    }

    /**
     * Sets position of the slider
     * @param x X coordinate of the slider's position
     * @param y Y coordinate of the slider's position
     */
    private void setSliderPosition(int x, int y) {
        slider.setOffset(x, y);
    }

    /**
     * When clicked, sets the sliding component of the slider and repaints the slider.
     * @param p Point that was clicked
     */
    protected void doClick(Point p) {
        slider.doClick(p);
        if (vertical) {
            setSliderPosition(0,
                              (int) Math.max(-slider.getHeight() / 2,
                                             Math.min(getHeight() - slider.getHeight()
                                                              / 2,
                                                      p.getY() - getY()
                                                              - slider.getHeight() / 2)));
        } else {
            setSliderPosition((int) Math.max(-slider.getWidth() / 2,
                                             Math.min(getWidth() - slider.getWidth() / 2,
                                                      p.getX() - getX()
                                                              - slider.getWidth() / 2)),
                              -slider.getHeight() * 2 / 5);
        }
        repaint();
    }

    /**
     * Does release when released. 
     */
    protected void doRelease() {
        slider.doRelease();
    }

    /**
     * Checks to see if the slider contains a point
     * @param p Point to check
     * @return a boolean that shows whether a slider has a point
     */
    public boolean contains(Point p) {
        return super.contains(p) || slider.contains(p);
    }

    /**
     * @return the min, current, and max values
     */
    public String toString() {
        return min + " " + getValue() + " " + max;
    }
}
